package com.jh.wdbc.model;

import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;

public class MapList<T> extends ArrayList<T> implements List<T>, RandomAccess, Cloneable, Serializable {

	private static final long serialVersionUID = -8296802777451941013L;

	private Map<String, List<T>> map = new HashMap<String, List<T>>();
	private String keyFieldName = null;

	public List<T> getListForKey(String key) {
		return map.get(key);
	}

	public Set<String> keySet() {
		return map.keySet();
	}

	@Override
	public boolean add(T element) {

		addToMap(element);
		return super.add(element);
	}

	@Override
	public boolean addAll(Collection<? extends T> paramCollection) {
		addToMap(paramCollection);
		return super.addAll(paramCollection);
	}

	@Override
	public boolean addAll(int paramInt, Collection<? extends T> paramCollection) {
		addToMap(paramCollection);
		return super.addAll(paramInt, paramCollection);
	}

	private void addToMap(Collection<? extends T> collection) {
		for (T e : collection) {
			addToMap(e);
		}
	}

	private void addToMap(T element) {
		String key = getKey(element);
		if (!map.containsKey(key)) {
			map.put(key, new ArrayList<T>());
		}
		List<T> listForKey = map.get(key);
		listForKey.add(element);
	}

	private String getKey(T element) {
		Class<? extends Object> clazz = element.getClass();
		if (keyFieldName == null) {
			keyFieldName = detectKeyFieldName(clazz);
		}
		Field field;
		try {
			field = clazz.getDeclaredField(keyFieldName);

			field.setAccessible(true);
			Object key = field.get(element);
			return (String)key;
		} catch (Exception e) {
		}
		return null;
	}

	private String detectKeyFieldName(Class<? extends Object> clazz) {
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.getAnnotation(Key.class) != null) {
				return field.getName();
			}
		}
		return null;
	}

	@Override
	public boolean remove(Object paramObject) {
		@SuppressWarnings("unchecked") T param = (T)paramObject;
		String key = getKey(param);
		List<T> listForKey = map.get(key);
		listForKey.remove(paramObject);
		if(listForKey.size() == 0){
			map.remove(key);
		}
		return super.remove(paramObject);
	}

	@Target({ElementType.FIELD})
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface Key{
	}
}
